#ifndef M3U8PARSER_H
#define M3U8PARSER_H
#include <QString>
#include <QList>
#include <QVector>
#include <QDebug>
#include <QDir>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QObject>

enum class NodeStatu {
    UNDOWNLOAD = 0,
    DOWNLOADING,
    DOWNLOADED
};

struct M3u8Node {
    int piece_idx;      // 分片序号
    NodeStatu download_statu;//是否完成下载
    QString url;        // 分片请求URL
    QString path;       // 已下载分片的存放路径
    float duration;       // 分片时长
    float stime;
    int offset;         //播放偏移量，只有在seek跳转的时候才有用
    bool is_seek;
    M3u8Node():piece_idx(-1){}
    M3u8Node(int idx, NodeStatu statu, const QString &u, const QString &p, float d, float s)
        : piece_idx(idx)
        , download_statu(statu)
        , url(u)
        , path(p)
        , duration(d)
        , stime(s)
        , offset(0)
        , is_seek(false){
        qDebug() << "分片: idx=" << idx << "; url=" << u << "; path=" << p << "; duration=" << d << "stime=" << s << "; statu: " << (int)statu;
    }
};

class M3u8Parser : public QObject
{
    Q_OBJECT
public:
    M3u8Parser(const QString &store_path)
        :_is_m3u8(false)
        ,_total_duration(0)
        ,_piece_count(0)
        ,_base_store_path(store_path){
        createDir(_base_store_path);
    }
    int duration() {
        return _total_duration;
    }
    int pieceCount() {
        return _piece_count;
    }
    bool piece(int idx, M3u8Node& piece) {
        if (idx >= _piece_count) {
            qDebug() << "请求的分片ID大于分片总数！" << idx;
            return false;
        }
        piece = _pieces[idx];
        return true;
    }
    void reset() {
        _is_m3u8 = false;
        _total_duration = 0;
        _piece_count = 0;
        _version = "";
        _pieces.clear();
    }
    bool hlsRequest(const QString &url){
        reset();
        qDebug() << "开始请求HLS网络视频:" << url ;
        QNetworkRequest request;
        request.setUrl(QUrl(url));
        QNetworkReply *reply = manager.get(request);
        connect(reply, &QNetworkReply::finished, this, [=](){
            if (reply->error() != QNetworkReply::NoError) {
                qDebug() << "HLS请求失败: " << reply->errorString();
                emit hlsRequestFailedSignal(reply->errorString());
                return reply->deleteLater();
            }
            QString res = QString::fromUtf8(reply->readAll());
            bool ret = this->parse(res);
            if (ret == false) {
                emit hlsRequestFailedSignal("请求到的文件非M3U8配置文件！");
                return reply->deleteLater();
            }
            qDebug() << "请求到的M3U8配置文件解析完毕，发送HLS请求成功信号";
            emit hlsRequestSuccessSignal();
            return reply->deleteLater();
        });
        return true;
    }
    bool downloadPiece(int idx) {
        if (idx < 0 || idx >= _piece_count) {
            qDebug() << "请求下载的分片ID不合法！" << idx;
            emit pieceDownloadFailSignal(idx, "视频分片下载失败：分片ID不合法");
            return false;
        }
        if (_pieces[idx].download_statu == NodeStatu::DOWNLOADING) {
            qDebug() << "分片正在下载中！" << idx;
            return true;
        } else if (_pieces[idx].download_statu == NodeStatu::DOWNLOADED) {
            qDebug() << "分片已经下载过了！" << idx;
            emit pieceDownloadSuccessSignal(idx);
            return true;
        }
        qDebug() << "开始请求下载分片: " << idx;
        _pieces[idx].download_statu = NodeStatu::DOWNLOADING;
        QNetworkRequest request;
        request.setUrl(QUrl(_pieces[idx].url));
        QNetworkReply *reply = manager.get(request);
        connect(reply, &QNetworkReply::finished, this, [=](){
            if (reply->error() != QNetworkReply::NoError) {
                qDebug() << "视频分片下载失败: " << reply->errorString();
                emit pieceDownloadFailSignal(idx, "视频分片下载失败");
                return reply->deleteLater();
            }
            QFile file(_pieces[idx].path);
            if (file.open(QIODevice::WriteOnly) == false){
                qDebug() << "打开分片文件失败: " << _pieces[idx].path;
                emit pieceDownloadFailSignal(idx, "视频分片存储失败");
                return reply->deleteLater();
            }
            file.write(reply->readAll());
            file.close();
            qDebug() << "分片下载成功：" << idx;
            _pieces[idx].download_statu = NodeStatu::DOWNLOADED;
            emit pieceDownloadSuccessSignal(idx);
            return reply->deleteLater();
        });
        return true;
    }
    bool seek(int pos, M3u8Node &piece) {
        if (pos > _total_duration) {
            qDebug() << "请求位置大于总时长！";
            return false;
        }
        float tp = pos;
        for (int i = 0; i < _pieces.size(); i++) {
            if (tp - _pieces[i].duration < 0) {
                this->piece(i, piece);
                piece.offset = tp;
                piece.is_seek = true;
                break;
            }
            tp -= _pieces[i].duration;
        }
        qDebug() << "跳转位置: pos=" << pos << "; index=" << piece.piece_idx << "; stime=" << piece.stime << "; offset=" << piece.offset;
        return true;
    }
private:
    bool parse(const QString &data){
        qDebug() << "开始解析m3u8配置文件！";
        createDir(_base_store_path);
        const QString m3u8type_filed = "#EXTM3U"; //表示这是个m3u8文件
        const QString version_filed = "#EXT-X-VERSION:";
        const QString piece_filed = "#EXTINF:"; //指定媒体段的时长
        int pos;
        QList<QString> list = data.split("\n");
        for (int i = 0; i < list.size(); i++) {
            QString str = list[i];
            if (str == m3u8type_filed) {
                _is_m3u8 = true;
            } else if ((pos = str.indexOf(version_filed)) != -1) {
                _version = str.mid(pos + version_filed.size());
            }else if ((pos = str.indexOf(piece_filed)) != -1) {
                QString duration = str.mid(pos + piece_filed.size());
                if (duration.back() == ',') duration.removeLast();
                QString piece_url = list[++i];
                QString store_path = _base_store_path + getName(piece_url);
                NodeStatu complated = NodeStatu::UNDOWNLOAD;
                if (exists(store_path)) complated = NodeStatu::DOWNLOADED;
                float fd = duration.toFloat();
                M3u8Node node(_piece_count++, complated, piece_url, store_path, fd, _total_duration);
                _pieces.push_back(node);
                _total_duration += fd;
            }
        }
        qDebug() << "视频总时长:" << _total_duration;
        return _is_m3u8;
    }
    QString getPath(const QString &filename) {
        int pos = filename.lastIndexOf('/');
        return filename.mid(0, pos);
    }
    QString getName(const QString &filename) {
        int pos = filename.lastIndexOf('/');
        if (pos == -1) {
            return filename;
        }
        return filename.mid(pos + 1);
    }
    bool exists(const QString &filename) {
        QFile file(filename);
        return file.exists();
    }
    void createDir(const QString &path) {
        QDir dir(path);
        if (!dir.exists()) {
            dir.mkdir(path);
        }
    }
signals:
    void hlsRequestSuccessSignal();
    void hlsRequestFailedSignal(const QString msg);
    void pieceDownloadSuccessSignal(int idx);
    void pieceDownloadFailSignal(int idx, const QString &msg);
private slots:
private:
    QNetworkAccessManager manager;
    bool _is_m3u8;
    float _total_duration;
    int _piece_count;
    QString _version;
    QString _base_store_path;
    QVector<M3u8Node> _pieces;
};

#endif // M3U8PARSER_H
